#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016-2018, Niklas Hauser
# Copyright (c) 2017, Fabian Greif
# Copyright (c) 2022-2023, Christopher Durand
#
# This file is part of the modm project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# -----------------------------------------------------------------------------

class Instance(Module):
    def __init__(self, instance):
        self.instance = instance

    def init(self, module):
        module.name = str(self.instance)
        module.description = "Instance {}".format(self.instance)

    def prepare(self, module, options):
        module.depends(":platform:adc", ":math:algorithm")
        return True

    def build(self, env):
        device = env[":target"]
        driver = device.get_driver("adc")

        properties = device.properties
        properties["target"] = target = device.identifier
        instance_id = int(self.instance)
        properties["id"] = instance_id

        if target["family"] == "h7":
            if (instance_id != 3) or (target["name"][0] in ["4", "5"]):
                properties["resolution"] = 16
            else:
                properties["resolution"] = 12
        else:
            properties["resolution"] = 12

        if instance_id == 1:
            if target["family"] == "f3":
                # 13-14 reserved
                channels = [1,2,3,4,5,6,7,8,9,10,11,12,15,16,17,18]
            elif target["family"] in ["l4", "l5"]:
                # ADC1 is connected to 16 external channels + 3 internal channels
                channels = range(1,17)
            elif target["family"] == "g4":
                # ADC1 is connected to 14 external channels + 4 internal channels
                # Channel 13: Opamp1
                # Channel 16: Temperature
                # Channel 17: VBat/3
                # Channel 18: VRefint
                channels = [1,2,3,4,5,6,7,8,9,10,11,12,14,15]
                assert(len(channels) == 14)
            elif target["family"] == "h7":
                channels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
            else:
                # 11-14 reserved
                channels = [1,2,3,4,5,6,7,8,9,10,15,16,17,18]
        elif instance_id == 2:
            if target["family"] == "f3":
                # 13-16 reserved
                channels = [1,2,3,4,5,6,7,8,9,10,11,12,17,18]
            elif target["family"] == "g4":
                # ADC2 is connected to 16 external channels + 2 internal channels
                # Channel 16: Opamp2
                # Channel 18: Opamp3
                channels = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17]
                assert(len(channels) == 16)
            elif target["family"] == "l5":
                # ADC1 is connected to 16 external channels + 2 internal channels
                channels = range(1,17)
            elif target["family"] == "h7":
                if target["name"] in ["a3", "b0", "b3"]:
                    # Channel 14: VBAT/4
                    # Channel 15: DAC2 OUT1
                    # Channel 16: DAC1 OUT1
                    # Channel 17: DAC1 OUT2
                    # Channel 18: VSENSE
                    # Channel 19: VREFINT
                    channels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13]
                else:
                    # Channel 16: DAC1 OUT1
                    # Channel 17: DAC1 OUT2
                    channels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,18,19]
            else:
                # ADC2 is connected to 16 external channels + 2 internal channels
                channels = range(1,17)
        elif instance_id == 3:
            if target["family"] == "f3":
                channels = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
            elif target["family"] == "g4":
                # ADC3 is connected to 15 external channels + 3 internal channels
                # Channel 13: Opamp3
                # Channel 17: VBat/3
                # Channel 18: VRefint
                channels = [1,2,3,4,5,6,7,8,9,10,11,12,14,15,16]
                assert(len(channels) == 15)
            elif target["family"] == "h7":
                if properties["resolution"] == 16:
                    # Channel 17: VBAT/4
                    # Channel 18: VSENSE
                    # Channel 19: VREFINT
                    channels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
                else:
                    # Channel 16: VBAT/4
                    # Channel 17: VSENSE
                    # Channel 18: VREFINT
                    channels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
            else:
                # ADC3 is connected to 12 external channels + 4 internal channels
                channels = [1,2,3,4,6,7,8,9,10,11,12,13]
        elif instance_id == 4:
            if target["family"] == "g4":
                # ADC4 is connected to 16 external channels + 2 internal channels
                # Channel 17: Opamp6
                # Channel 18: VRefint
                channels = range(1,17)
                assert(len(channels) == 16)
            else:
                # 14-16 reserved
                channels = [1,2,3,4,5,6,7,8,9,10,11,12,13,17,18]
        elif instance_id == 5:
            if target["family"] == "g4":
                # ADC5 is connected to 13 external channels + 5 internal channels
                # Channel 3: Opamp5
                # Channel 4: Temperature
                # Channel 5: Opamp4
                # Channel 17: VBat/3
                # Channel 18: VRefint
                channels = [1,2,6,7,8,9,10,11,12,13,14,15,16]
                assert(len(channels) == 13)
            else:
                raise NotImplementedError
        properties["channels"] = sorted(channels)

        if target["family"] == "f3":
            properties["ahb"] = "AHB"
            if len(driver["instance"]) == 1:
                properties["adc_ccr"] = "ADC1_CCR"
                properties["adc_pre"] = "ADC1PRES"
                properties["id_common"] = "1"
                properties["id_common_u"] = "1_COMMON"
            elif instance_id in [1,2]:
                properties["adc_ccr"] = "ADC12_CCR"
                properties["adc_pre"] = "ADCPRE12"
                properties["id_common"] = "12"
                properties["id_common_u"] = "1_2_COMMON"
            elif instance_id in [3,4]:
                properties["adc_ccr"] = "ADC34_CCR"
                properties["adc_pre"] = "ADCPRE34"
                properties["id_common"] = "34"
                properties["id_common_u"] = "3_4_COMMON"
            properties["clock_mux"] = False
        elif target["family"] == "l4":
            properties["adc_ccr"] = "ADC_CCR"
            properties["ccipr"] = "CCIPR"
            if len(driver["instance"]) == 1 and "q5a" not in target.string:
                properties["id_common"] = "1"
                properties["id_common_u"] = "1_COMMON"
            elif len(driver["instance"]) == 2 or "q5a" in target.string:
                properties["id_common"] = "12"
                properties["id_common_u"] = "12_COMMON"
            else:
                properties["id_common"] = "123"
                properties["id_common_u"] = "123_COMMON"
            properties["clock_mux"] = (target["name"] not in ("12", "22"))
        elif target["family"] == "l5":
            properties["adc_ccr"] = "ADC_CCR"
            properties["ccipr"] = "CCIPR1"
            properties["ahb"] = "AHB2"
            properties["id_common"] = ""
            properties["id_common_u"] = "12_COMMON"
            properties["clock_mux"] = True
        elif target["family"] == "g4":
            properties["ahb"] = "AHB2"
            properties["adc_ccr"] = "ADC_CCR"
            properties["ccipr"] = "CCIPR"
            if instance_id in [1, 2]:
                properties["id_common"] = "12"
                properties["id_common_u"] = "12_COMMON"
            else:
                properties["id_common"] = "345"
                properties["id_common_u"] = "345_COMMON"
            properties["clock_mux"] = True
        elif target["family"] == "h7":
            properties["adc_ccr"] = "ADC_CCR"
            if target["name"] in ["a3", "b0", "b3"]:
                properties["ccipr"] = "SRDCCIPR"
            else:
                properties["ccipr"] = "D3CCIPR"
            if instance_id in [1, 2]:
                properties["ahb"] = "AHB1"
                properties["id_common"] = "12"
                properties["id_common_u"] = "12_COMMON"
            else:
                properties["ahb"] = "AHB4"
                properties["id_common"] = "3"
                properties["id_common_u"] = "3_COMMON"
            properties["clock_mux"] = True
        else:
            raise NotImplementedError

        env.substitutions = properties
        env.outbasepath = "modm/src/modm/platform/adc"

        global props
        properties["shared_irq_ids"] = props["shared_irq_ids"]
        props["instances"].append(self.instance)

        env.template("adc.hpp.in", "adc_{}.hpp".format(self.instance))
        env.template("adc_impl.hpp.in", "adc_{}_impl.hpp".format(self.instance))
        env.template("adc_interrupt.hpp.in", "adc_interrupt_{}.hpp".format(self.instance))
        env.template("adc_interrupt.cpp.in", "adc_interrupt_{}.cpp".format(self.instance))


def init(module):
    module.name = ":platform:adc"
    module.description = "Analog-to-Digital Converter (ADC)"

def prepare(module, options):
    device = options[":target"]
    if not device.has_driver("adc:stm32-f3") and not device.has_driver("adc:stm32-h7"):
        return False

    module.depends(
        ":architecture:adc",
        ":math:algorithm",
        ":architecture:delay",
        ":architecture:register",
        ":cmsis:device",
        ":platform:gpio",
        ":platform:rcc")

    global props
    props = {}
    props["instances"] = []

    shared_irqs = [v["name"] for v in device.get_driver("core")["vector"]]
    shared_irqs = [v for v in shared_irqs if v.startswith("ADC") and "_" in v]
    props["shared_irqs"] = {}
    props["shared_irq_ids"] = []
    for irq in shared_irqs:
        parts = irq[3:].split("_")
        shared_irqs_ids = (int(parts[0]), int(parts[1]))
        props["shared_irqs"][irq] = shared_irqs_ids
        props["shared_irq_ids"].extend(shared_irqs_ids)

    if device.identifier.family == "h7":
        props["shared_irqs"]["ADC"] = (1, 2)
        props["shared_irq_ids"].extend((1, 2))

    for instance in listify(device.get_driver("adc")["instance"]):
        module.add_submodule(Instance(int(instance)))

    return True

def build(env):
    device = env[":target"]
    driver = device.get_driver("adc")

    properties = device.properties
    properties["target"] = device.identifier
    properties["driver"] = driver

    global props
    properties["shared_irq_ids"] = props["shared_irq_ids"]
    properties["instances"] = props["instances"]
    properties["shared_irqs"] = props["shared_irqs"]

    env.substitutions = properties
    env.outbasepath = "modm/src/modm/platform/adc"

    if any(i in props["shared_irq_ids"] for i in props["instances"]):
        env.template("adc_shared_interrupts.cpp.in")
